package xyz.scootaloo.mono.base.service.async

import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.TimeUnit

/**
 * @author flutterdash@qq.com
 * @since 2021/9/27 10:08
 */

typealias Callback<T> = (value: T) -> Unit
typealias ErrorHandler = (throwable: Throwable) -> Unit

/**
 * 一个可执行任务的定义, 做为异步方法的补充.
 *
 * 可以指定多个回调函数, 当任务执行完毕, 由回调函数来处理任务的返回值.
 * 可以指定多个处理器, 当执行任务时抛出异常, 由处理器来处理异常.
 *
 * 设定回调的 [onNext] 和 [onError] 是非线程安全的,
 * 所以当 [TaskDefinition] 对象被提交到线程池之后, 不允许再注册回调
 *
 * 每个回调都会被调度到线程池执行
 *
 * @param task      一个有返回值的方法
 * @param callbacks 一组回调, 用于处理 [task] 的返回值
 * @param handlers  一组处理器, 用于处理执行 [task] 时抛出的异常,
 *                  当没有注册任何处理器时, 会注册一个默认的处理器来打印调用栈
 */
class TaskDefinition<T>(
    private val task: () -> T,
    private val callbacks: ArrayList<Callback<T>> = ArrayList(1),
    private val handlers: ArrayList<ErrorHandler> = ArrayList(1),
    internal var scheduler: ScheduledExecutorService? = null
) {

    fun onNext(callback: Callback<T>): TaskDefinition<T> {
        callbacks.add(callback)
        return this
    }

    fun onError(handler: ErrorHandler): TaskDefinition<T> {
        handlers.add(handler)
        return this
    }

    internal fun buildRunnable(): () -> Unit {
        preset()
        return {
            try {
                val value = task()
                for (callback in callbacks) {
                    scheduler?.schedule { callback(value) }
                }
            } catch (throwable: Throwable) {
                for (handle in handlers) {
                    scheduler?.schedule { handle(throwable) }
                }
            }
        }
    }

    private fun preset() {
        if (handlers.isEmpty()) {
            handlers.add(DEFAULT_HANDLE)
        }
    }

    private fun ScheduledExecutorService.schedule(runnable: Runnable) {
        this.schedule(runnable, 0, TimeUnit.SECONDS)
    }

    companion object Statics {
        private val DEFAULT_HANDLE: ErrorHandler = { throwable ->
            throwable.printStackTrace()
        }
    }
}
